package benchmark;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

import tools.Toolbox;


public abstract class AbstractTask implements Runnable {

	// master
	
	protected static int threadNumber;
	
	private static boolean isRunning;
	
	private static long maxInside;
	
	private long getMaxInside() {
		return maxInside;
	}
	
	
	// master & sub
	
	public long duration;

	protected List<String> args;

	public AbstractTask(List<String> args) {
		this.args = args;
	}

	// sub
	
	protected int threadID = 0;
	protected AbstractTask(Integer threadID) {
		this.threadID = threadID;
	}	
			
	public long compute() throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		while (isRunning) {
			Thread.sleep(200);
		}
		isRunning = true;
		
//		beforeCompute();
		
//		initWorkingArray();
		
		Thread[] threads = new Thread[threadNumber];
		List<AbstractTask> tasks = new ArrayList<AbstractTask>();
		
		Constructor<?> ctor = null;
		Class<?>[] ctorArgs = new Class[1];
		ctorArgs[0] = Integer.class;
		ctor = this.getClass().getDeclaredConstructor(ctorArgs);
//		for(Constructor<?> c : this.getClass().getDeclaredConstructors()) {
////			Class<?>[] a = c.getParameterTypes();
////			for (Class<?> clazz : a) {
////				System.out.print(clazz.getName());
////			}
////			System.out.println();
//			if (c.getParameterTypes().length == 2) {
//				ctor = c;
//			}
//		}
		
		for (int i = 0; i < threadNumber; i++) {
//			Class<?>[] ctorArgs = new Class[1];
//			ctorArgs[0] = Integer.class;
			
						
			AbstractTask task = (AbstractTask) ctor.newInstance(new Object[]{i});
			tasks.add(task);
			threads[i] = new Thread(task);
		}
		
		long startTime = System.nanoTime();
		
		for (Thread t : threads) {
			t.start();
		}
		
		for (Thread t : threads) {
			t.join();
		}
		
		long endTime = System.nanoTime();
		
		//timing out side the threads
		duration = endTime - startTime;
		
		//timing inside the threads
		maxInside = 0;
		for (AbstractTask task : tasks) {
			if(task.duration > maxInside) {
				maxInside = task.duration;
			}
		}
		
//		afterCompute();

		isRunning = false;
		return duration;
	}
	
//	public abstract String getLog();
	
//	protected abstract void afterCompute();
	
	
	
	public String compute(int repeatTime) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, InterruptedException {
		long[] results = new long[repeatTime];
		long[] maxInsides = new long[repeatTime];
		for (int i = 0; i < repeatTime; i++) {
			results[i] = compute();
			maxInsides[i] = this.getMaxInside();
		}
		
		long result = Toolbox.getMedium(results);
		long maxInside = Toolbox.getMedium(maxInsides);
		
		String record = "";
		for (String a : args) {
			record += a + "\t";
		}
		record += result + "\t";
		record += maxInside + "\n";
				
		return record;

	}
	
	


}
